package com.wy.tcp;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

/**
 * @author wangyong
 */
@Slf4j
public class TcpProxyServer {

    private Bootstrap bootstrap;
    private ServerBootstrap server;

    private NioEventLoopGroup bossgroup;
    private NioEventLoopGroup workgroup;

    private int exPort;
    private String destHostIP;
    private int destPort;

    public TcpProxyServer(ProxyParaModel paraModel) {
        this.exPort = paraModel.getLocalPort();
        this.destHostIP = paraModel.getRemoteHost();
        this.destPort = paraModel.getRemotePort();
    }

    public void start() {

        this.bossgroup = new NioEventLoopGroup();
        this.workgroup = new NioEventLoopGroup();
        this.server = new ServerBootstrap();

        try {
            this.bootstrap = new Bootstrap();
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.group(bossgroup);

            this.server.group(bossgroup, workgroup);
            server.channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast("serverHandler", new TcpMessageHandler(getClientChannel(ch)));
                        }
                    }).option(ChannelOption.SO_BACKLOG, 1024)
                    .option(ChannelOption.SO_SNDBUF, 16 * 1024)
                    .option(ChannelOption.SO_RCVBUF, 16 * 1024)
                    .option(ChannelOption.SO_KEEPALIVE, true);
            server.bind(exPort).syncUninterruptibly()
                    .addListener((ChannelFutureListener) channelFuture -> {
                        if (channelFuture.isSuccess()) {
                            log.info("端口 {} 代理服务器启动成功", this.exPort);
                        } else {
                            log.info("端口 {} 代理服务器启动失败", this.exPort);
                        }
                    });

        } catch (Exception e) {
            log.info("端口 {} 代理服务器启动失败 :{}",this.exPort, e);
        }
    }

    private Channel getClientChannel(SocketChannel ch) throws InterruptedException {
        this.bootstrap
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) {

                        socketChannel.pipeline().addLast("clientHandler", new TcpMessageHandler(ch));
                    }
                });
        ChannelFuture sync = bootstrap.connect(destHostIP, destPort).sync();
        return sync.channel();
    }

}